using System;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace SmallMvc
{
    public class UrlResolver
    {
        private readonly IHttpContextProvider _httpContextProvider;

        public UrlResolver(IHttpContextProvider httpContextProvider)
        {
            _httpContextProvider = httpContextProvider;
        }

        public string UrlFor<CONTROLLER>(Expression<Func<CONTROLLER, object>> actionExpression)
        {
            var route = RouteCollectionExtentions.GetRouteValues(actionExpression);

            var context = _httpContextProvider.GetContext();
            var path = RouteTable.Routes.GetVirtualPath(new RequestContext(context, RouteTable.Routes.GetRouteData(context)), route);

            if (path != null)
            {
                return path.VirtualPath;
            }


            return null;
        }


    }

    public static class RouteCollectionExtentions
    {

        public static RouteValueDictionary GetRouteValues<CONTROLLER>(Expression<Func<CONTROLLER, object>> expression)
        {
            var call = expression.Body as MethodCallExpression;
            var controllerName = typeof(CONTROLLER).Name;
            controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length);

            if (call != null)
            {
                var route = new RouteValueDictionary { { "controller", controllerName }, { "action", call.Method.Name } };

                var parameters = call.Method.GetParameters();

                if (parameters.Length > 0)
                {
                    var paramLength = parameters.Length;
                    for (int i = 0; i < paramLength; i++)
                    {
                        object value;
                        Expression arg = call.Arguments[i];
                        var ce = arg as ConstantExpression;
                        if (ce != null)
                        {
                            value = ce.Value;
                        }
                        else
                        {
                            Expression<Func<object>> lambda = Expression.Lambda<Func<object>>(Expression.Convert(arg, typeof(object)),
                                                                                              new ParameterExpression[0]);
                            try
                            {
                                value = lambda.Compile()();
                            }
                            catch
                            {
                                value = string.Empty;
                            }
                        }
                        route.Add(parameters[i].Name, value);

                    }
                }

                return route;
            }

            return null;
        }
    }
}